package com.zy.core;

import com.zy.annotation.Autowired;
import com.zy.annotation.Controller;
import com.zy.annotation.RequestMapping;
import com.zy.annotation.Service;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

public class Dispatcherservlet extends HttpServlet {
    private Properties properties = new Properties();
    //存放所有.class文件的文件名
    private List<String> classNames = new ArrayList<>();
    //存放所有controller方法映射的路径
    private Map<String, Method> handMapping = new HashMap<>();
    //存放所有bean的实例
    private Map<String, Object> ioc = new HashMap<>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        doLoading(config);
        String scannerPackage = properties.getProperty("ScannerPackage");
        scannerPackage = scannerPackage.replaceAll("\\.", "/");
        doScannerPackage(scannerPackage);
        doIOC();
        doAutowired();
        doHanderMapping();
        System.out.println("初始化已完成...");
    }

    /**
     * 扫描包下所有的.class文件
     * @param scannerPackage
     */
    private void doScannerPackage(String scannerPackage) {
        URL url = this.getClass().getClassLoader().getResource(scannerPackage);
        File file = new File(url.getFile());
        if (file.exists()) {
            File[] files = file.listFiles(file1 -> file.getName().endsWith(".class") || file.isDirectory());
            for (File f : files) {
                if (f.isDirectory()) {
                    doScannerPackage(scannerPackage + "/" + f.getName());
                } else {
                    String className = f.getName().split("\\.")[0];
                    classNames.add(scannerPackage.replaceAll("/", "\\.") + "." + className);
                }
            }
        }
    }

    /**
     * 在ioc容器对加上Controller、Service、Autowired注解的类或字段中注入bean的实例对象
     */
    private void doIOC() {
        for (String className : classNames) {
            try {
                Class<?> aClass = Class.forName(className);
                String beanName = className.substring(className.lastIndexOf("\\.") + 1);
                beanName = beanName.substring(0, 1).toLowerCase() + beanName.substring(1);
                if (aClass.isAnnotationPresent(Controller.class) || aClass.isAnnotationPresent(Service.class)) {
                    ioc.put(beanName, aClass.newInstance());
                    Field[] fields = aClass.getDeclaredFields();
                    for (Field field : fields) {
                        if (field.isAnnotationPresent(Autowired.class)) {
                            String fieldName = field.getName();
                            ioc.put(fieldName, field.getType().newInstance());
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }

    /**
     * 通过处理器映射器映射前端传过来的url路径
     */
    private void doHanderMapping() {
        Set<Map.Entry<String, Object>> entries = ioc.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            Class<?> aClass = entry.getValue().getClass();
            if (aClass.isAnnotationPresent(Controller.class)) {
                Controller controller = aClass.getAnnotation(Controller.class);
                String controllerUrl = controller.value();
                Method[] methods = aClass.getDeclaredMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                        String methodUrl = requestMapping.value();
                        handMapping.put(controllerUrl+methodUrl, method);
                    }
                }
            }
        }
    }

    /**
     * 为controller和service注解的类中加上Autowired字段的对象赋值(从ioc容器中取出实例对象)
     */
    private void doAutowired() {
        Set<Map.Entry<String, Object>> entries = ioc.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            Class<?> aClass = entry.getValue().getClass();
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    try {
                        field.setAccessible(true);
                        field.set(entry.getValue(),ioc.get(field.getName()));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }

    /**
     * 加载properties配置文件
     * @param config
     */
    private void doLoading(ServletConfig config) {
        String scannerPackage = config.getInitParameter("ScannerPackage");
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(scannerPackage);
        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 反射代理执行controller中标记RequestMapping注解的方法
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String uri = request.getRequestURI();
        if (handMapping.containsKey(uri)) {
            Method method = handMapping.get(uri);
            System.out.println(uri);
            Class<?> aClass = method.getDeclaringClass();
            String className = aClass.getName();//com.zy.controller.TestController
            String beanName = className.substring(className.lastIndexOf("\\.") + 1);//TestController
            beanName = beanName.substring(0,1).toLowerCase() + beanName.substring(1); //testController
            try {
                method.invoke(ioc.get(beanName),request,response);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}
